package relay_switch

import (
	"device-modbus/core"
	"device-modbus/internal/biz/model"
	"encoding/binary"
	"fmt"
	"time"

	"github.com/cloudwego/hertz/pkg/common/hlog"
)

// RelaySwitchProtocol 继电器开关协议实现（支持06功能码控制命令）
// 适配命令格式：05 06 00 00 00 01 49 8E（开机命令）
type RelaySwitchProtocol struct{}

// 编译时类型检查：确保NewProtocol符合core.ProtocolCreator类型
var _ core.ProtocolCreator = NewProtocol

// NewProtocol 导出插件创建函数（签名严格匹配core.ProtocolCreator）
func NewProtocol() core.SensorProtocol {
	return &RelaySwitchProtocol{}
}

// GetType 返回设备类型（继电器开关）
func (p *RelaySwitchProtocol) GetType() string {
	return "modbus"
}

// GetModel 返回继电器型号（通用Modbus继电器）
func (p *RelaySwitchProtocol) GetModel() string {
	return "relay_switch"
}

// GetProtocolName 返回协议唯一标识
func (p *RelaySwitchProtocol) GetProtocolName() string {
	return "继电器"
}

// RelaySwitchData 继电器开关数据结构
type RelaySwitchData struct {
	DeviceAddr byte   // 设备地址
	FuncCode   byte   // 功能码
	RegAddr    uint16 // 寄存器地址
	State      string // 开关状态（"ON"/"OFF"）
	RawState   uint16 // 原始状态值（0001/0000）
}

// BuildRequest 构建继电器控制命令
// 参数说明：
// - deviceAddr: 设备地址
// - funcCode: 功能码（固定06）
// - startAddr: 寄存器地址
// - dataValue: 控制值（0001=开，0000=关）
func (p *RelaySwitchProtocol) BuildRequest(deviceAddr, funcCode byte, startAddr, dataValue uint16) ([]byte, error) {
	// 校验功能码必须为06（强制单线圈操作）
	if funcCode != 0x06 {
		return nil, fmt.Errorf("%w: 继电器仅支持0x06功能码，实际0x%02X", core.ErrorUnsupported, funcCode)
	}

	hlog.Debugf("构建继电器控制命令 | 设备地址:0x%02X, 功能码:0x%02X, 寄存器:0x%04X, 控制值:0x%04X",
		deviceAddr, funcCode, startAddr, dataValue)

	// 构建命令帧主体（6字节）：地址+功能码+寄存器地址+控制值
	frame := make([]byte, 6)
	frame[0] = deviceAddr                             // 设备地址
	frame[1] = funcCode                               // 功能码0x06
	binary.BigEndian.PutUint16(frame[2:4], startAddr) // 寄存器地址
	binary.BigEndian.PutUint16(frame[4:6], dataValue) // 控制值

	// 计算CRC校验
	crc := calculateCRC(frame)
	frame = append(frame, byte(crc&0xFF))    // CRC低字节
	frame = append(frame, byte(crc>>8&0xFF)) // CRC高字节

	// 验证开机命令是否匹配示例
	if deviceAddr == 0x05 && funcCode == 0x06 && startAddr == 0x0000 && dataValue == 0x0001 {
		hlog.Debugf("生成开机命令帧 | 十六进制: %X（与示例命令一致）", frame)
	}
	return frame, nil
}

// ParseResponse 解析继电器响应帧
func (p *RelaySwitchProtocol) ParseResponse(resp []byte) (*model.SensorData, error) {
	// 校验响应帧长度（固定8字节：地址1+功能码1+寄存器2+值2+CRC2）
	const expectedLen = 8
	if len(resp) != expectedLen {
		return nil, fmt.Errorf("%w: 响应帧长度应为%d字节，实际%d字节", core.ErrorInvalidResponse, expectedLen, len(resp))
	}

	// 校验设备地址
	if resp[0] != 0x05 {
		return nil, fmt.Errorf("%w: 响应设备地址应为0x05，实际0x%02X", core.ErrorInvalidResponse, resp[0])
	}

	// 校验功能码
	if resp[1] != 0x06 {
		return nil, fmt.Errorf("%w: 响应功能码应为0x06，实际0x%02X", core.ErrorInvalidResponse, resp[1])
	}

	// CRC校验
	crcCheckData := resp[:6] // 前6字节为数据主体
	expectedCRC := calculateCRC(crcCheckData)
	actualCRC := uint16(resp[6]) | uint16(resp[7])<<8
	if actualCRC != expectedCRC {
		return nil, fmt.Errorf("%w: CRC校验失败 | 预期0x%04X，实际0x%04X", core.ErrorInvalidResponse, expectedCRC, actualCRC)
	}

	// 解析开关状态
	regAddr := binary.BigEndian.Uint16(resp[2:4])
	rawState := binary.BigEndian.Uint16(resp[4:6])
	state := "OFF"
	if rawState == 0x0001 {
		state = "ON"
	}

	// 封装解析结果
	parsed := &RelaySwitchData{
		DeviceAddr: resp[0],
		FuncCode:   resp[1],
		RegAddr:    regAddr,
		State:      state,
		RawState:   rawState,
	}

	hlog.Debugf("解析继电器响应 | 设备地址:0x%02X, 寄存器:0x%04X, 状态:%s（原始值:0x%04X）",
		parsed.DeviceAddr, parsed.RegAddr, parsed.State, parsed.RawState)

	// 转换为统一的SensorData结构
	return &model.SensorData{
		SensorID:  fmt.Sprintf("%d-relay", parsed.DeviceAddr),
		Type:      p.GetType(),
		Model:     p.GetModel(),
		Timestamp: time.Now().UnixMilli(),
		RawData:   resp,
		Values: map[string]string{
			"state": parsed.State,
		},
		Protocol: p.GetProtocolName(),
	}, nil
}

// calculateCRC 计算Modbus CRC16校验码（多项式0xA001）
// 验证示例：命令主体05 06 00 00 00 01的CRC为0x498E
func calculateCRC(data []byte) uint16 {
	crc := uint16(0xFFFF)
	for _, b := range data {
		crc ^= uint16(b)
		for i := 0; i < 8; i++ {
			if crc&0x0001 != 0 {
				crc >>= 1
				crc ^= 0xA001 // Modbus标准多项式
			} else {
				crc >>= 1
			}
		}
	}
	return crc
}
